/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.iecg.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.iecg.api.dto.IecgCheckReportDto;
import com.iecg.api.dto.IecgNoticeDto;
import com.iecg.api.entity.IecgCheckCondition;
import com.iecg.api.entity.IecgCheckData;
import com.iecg.api.entity.IecgCheckReport;
import com.iecg.api.entity.IecgUser;
import com.iecg.dao.IecgCheckReportRepository;
import com.iecg.service.IecgCheckConditionService;
import com.iecg.service.IecgCheckDataService;
import com.iecg.service.IecgCheckReportService;
import com.iecg.service.IecgUserService;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import top.iecg.cascare.ZKHSCascare;

import java.util.*;

;

/**
 *
 *
 * @author zjc
 * @date 2019-09-05 11:07:08
 */
@Service
@Slf4j
public class IecgCheckReportServiceImpl implements IecgCheckReportService {

	@Autowired
	private IecgCheckReportRepository checkReportRepository;

	@Autowired
	private IecgCheckDataService iecgCheckDataService;

	@Autowired
	private IecgCheckConditionService iecgCheckConditionService;

	@Autowired
	private MongoTemplate mongoTemplate;

	@Autowired
	private RedisTemplate redisTemplate;

	private IecgUserService iecgUserService;

	public IecgCheckReport save(IecgCheckReport iecgCheckReport) {
		return checkReportRepository.save(iecgCheckReport);
	}

	public Optional<IecgCheckReport> findById(String id) {
		return checkReportRepository.findById(id);
	}

	@Override
	public Optional<IecgCheckReport> findByBizId(String bizId) {
		IecgCheckReport iecgCheckReport = new IecgCheckReport();
		iecgCheckReport.setBizId(bizId);
		Example<IecgCheckReport> example = Example.of(iecgCheckReport);
		return checkReportRepository.findOne(example);
	}

	@Override
	public Page<IecgCheckReport> page(Pageable pageable, IecgCheckReport iecgCheckReport) {

		Query query = new Query();

		//动态拼接查询条件
//		if (!StringUtils.isEmpty(studentReqVO.getName())){
//			Pattern pattern = Pattern.compile("^.*" + studentReqVO.getName() + ".*$", Pattern.CASE_INSENSITIVE);
//			query.addCriteria(Criteria.where("name").regex(pattern));
//		}

		if (iecgCheckReport.getDeviceId()!=null){
			query.addCriteria(Criteria.where("deviceId").is(iecgCheckReport.getDeviceId()));
		}

		if(iecgCheckReport.getUserId() != null){
			query.addCriteria(Criteria.where("userId").is(iecgCheckReport.getUserId()));
		}

		//计算总数
		long total = mongoTemplate.count(query, IecgCheckReport.class);

		//查询结果集
		List<IecgCheckReport> resultList = mongoTemplate.find(query.with(pageable), IecgCheckReport.class);

		Page<IecgCheckReport> pages = new Page(pageable.getPageNumber(), pageable.getPageSize(), total);
		pages.setRecords(resultList);
		pages.setSearchCount(true);
		return pages;
	}

	public IecgCheckReport modifyCheckReport(IecgCheckReport iecgCheckReport) {
		return checkReportRepository.save(iecgCheckReport);
	}

	public void deleteCheckReport(String id) {
		checkReportRepository.deleteById(id);
	}

	private double findAverage(Double[] array){
		double sum = 0;
		int count = 0;
		for(int i = 0; i < array.length; i++){
			if(60 /array[i] <= 200 && 60 / array[i] >= 30){
				count ++;
				sum += array[i];
			}
		}
		return sum/count;
	}

	@Override
	public Boolean isNotConformityRule(IecgCheckReportDto iecgCheckReportDto){
		String bizId = iecgCheckReportDto.getBizId();
		List<IecgCheckData> ecgDataList = iecgCheckDataService.findAllByBizId(bizId);
		if(ecgDataList.isEmpty()){
			return true;
		}
		Boolean present = ecgDataList.stream().filter(x -> {
			if (x.getEcgData().length() > 0 && x.getEcgData().length() < 400) {
				return true;
			}
			return false;
		}).findAny().isPresent();
		if(present){
			return true;
		}
		produceReport(iecgCheckReportDto);
		return false;
	}

	/**
	 * 生成心电报告
	 * @param iecgCheckReportDto
	 */
	@Async
	@SneakyThrows
	public void produceReport(IecgCheckReportDto iecgCheckReportDto) {
		String bizId = iecgCheckReportDto.getBizId();

		// test data
		//String maskBizId = "5da004be2684174adcdb066c";
		int gender = 0;
		int age = 41;
		//---------------------------------------------------------

		List<IecgCheckData> ecgDataList = iecgCheckDataService.findAllByBizId(bizId);
        String ecgdata = ecgDataList.get(0).getEcgData();
        int gain = ecgDataList.get(0).getGain();
        int fs = ecgDataList.get(0).getSamplingRate();
        int zeroValue = ecgDataList.get(0).getZeroValue();
		String[]  stringArray = ecgdata.split(",");
		double[] doubleArray = new double[stringArray.length];
		for(int i=0;i< doubleArray.length;i++){
			double originalData = Double.parseDouble(stringArray[i]);
			doubleArray[i] = (originalData - zeroValue)/gain;
			//log.info(String.valueOf(doubleArray[i]));
		}
		log.info(System.getProperty("java.library.path"));
		double[] filterecgdata = ZKHSCascare.ecgFilter(doubleArray, doubleArray.length, fs);
		double[] hrs= ZKHSCascare.getHeartRate(filterecgdata, filterecgdata.length, fs, gain);
		List<Double> tempList = new ArrayList<>();
		for (int i = 0; i < hrs.length ; i++) {
			if(60 / hrs[i] > 30 && 60 / hrs[i] < 200){
				tempList.add(hrs[i]);
			}
			log.info(String.valueOf(hrs[i]));
		}
		Double[] filterHrs = tempList.toArray(new Double[tempList.size()]);
 		double average = findAverage(filterHrs);
		IecgCheckCondition iecgCheckCondition = iecgCheckConditionService.findById(bizId).get();

		IecgCheckReport iecgCheckReport = new IecgCheckReport();
		iecgCheckReport.setAvgHr((int) (60/average));
		Arrays.sort(filterHrs);
		iecgCheckReport.setMaxHr((int) (60/(filterHrs[0])));
		iecgCheckReport.setMinHr((int) (60/filterHrs[filterHrs.length-1]));
		iecgCheckReport.setBizId(bizId);
		iecgCheckReport.setMode(iecgCheckReportDto.getMode());
		iecgCheckReport.setOutliers(iecgCheckReportDto.getOutliers());


		int signalsNum = ecgDataList.size();

		int[] feeling={0,0,0,0,0};//检测前填写的体格检查
		int[] abnormalPoint={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//测试过程中用户异常打卡点,采样点的序号

		int[] fss=new int[signalsNum];
		double[] gains=new double[signalsNum];
		double[] zerovalue=new double[signalsNum];
		StringBuffer buf = new StringBuffer();
		for(int i=0; i< signalsNum; i++){
			if(i>0){
				buf.append("|");
			}
			buf.append(ecgDataList.get(i).getLeadType());
			fss[i]= ecgDataList.get(i).getSamplingRate();
			gains[i]= ecgDataList.get(i).getGain();
			zerovalue[i]=ecgDataList.get(i).getZeroValue();
		}
		double[][] multiEcgData = new double[signalsNum][];

		int samples= 0;
		for(int i = 0 ; i < signalsNum ; i++ ){
			String ecgData = ecgDataList.get(i).getEcgData();
			String[]  ecgArray = ecgData.split(",");
			Double[]  ecgDataArray = Arrays.stream(ecgArray).map(cell ->
				 (Double.valueOf(cell)- zeroValue) / gain).toArray(Double[]::new);
			multiEcgData[i] = ArrayUtils.toPrimitive(ecgDataArray);
			samples = ecgArray.length;
		}
		log.info("开始生成分析报告......................");
		StringBuffer sb = new StringBuffer();
		Arrays.stream(multiEcgData[0]).forEach(x->{
			sb.append(x).append(",");
		});
		log.info("心电数据:" + sb.toString());
		log.info("导联数:" + signalsNum);
		log.info("采样值：" +  samples);
		log.info("采样率:" + fss);
		log.info("增益:" + Arrays.toString(gains));
		log.info("零值:" + zeroValue);
		log.info("导联:" + buf.toString());
		log.info("年龄："+ age);
		log.info("性别：" + gender);
		log.info("测前检查:" + Arrays.toString(feeling));
		String result = ZKHSCascare.ecgAnalysisEcgfeatures(multiEcgData, signalsNum, samples, fss, gains, zerovalue, buf.toString(), age, gender, feeling, 5, abnormalPoint, 20);
		iecgCheckReport.setAnalyse(result);
		iecgCheckReport.setUserId(iecgCheckCondition.getUserId());
		iecgCheckReport.setDeviceId(iecgCheckCondition.getDeviceId());
		iecgCheckReport.setCreateTime(new Date());
		iecgCheckReport.setImg(iecgCheckReportDto.getImg());
		iecgCheckReport.setIecgCheckCondition(iecgCheckCondition);
		makeFileNo(iecgCheckReport);
		save(iecgCheckReport);
		// 更新检测时间
		try {
			IecgUser iecgUser = iecgUserService.getSecurityUser();
			iecgUser.setLastCheck(new Date());
			iecgUserService.saveOrUpdate(iecgUser);
		}catch (Exception e){

		}
		// 生成通知
		Long userId = iecgCheckCondition.getUserId();
		BoundHashOperations<String, String, IecgNoticeDto> boundHashOperations = redisTemplate.boundHashOps(CacheConstants.IECG_NOTICT + ":" + userId.toString());
		IecgNoticeDto iecgNoticeDto = new IecgNoticeDto();
		iecgNoticeDto.setMark(true);
		iecgNoticeDto.setBizId(iecgCheckReport.getBizId());
		iecgNoticeDto.setCretatTime(new Date());
		iecgNoticeDto.setMsg(String.format("生成了一份新的心电报告%s",iecgCheckReport.getFileNo()));
		boundHashOperations.put(iecgCheckReport.getBizId(), iecgNoticeDto);
	}

	/**
	 * 制作报告文件号
	 * @param iecgCheckReport
	 */
	private void makeFileNo(IecgCheckReport iecgCheckReport){
		Query query = new Query();
		query.addCriteria(Criteria.where("userId").is(iecgCheckReport.getUserId()));
		query.with(new Sort(new Sort.Order(Sort.Direction.DESC,"createTime"))).limit(1);
		IecgCheckReport lastReport = mongoTemplate.findOne(query, IecgCheckReport.class);

			if(lastReport == null || !DateUtil.isSameDay(lastReport.getCreateTime(), new Date())){
				String fileName =DateUtil.format(new Date(), "yyyyMMdd")+ "01" ;
				iecgCheckReport.setFileNo(Integer.parseInt(fileName));
			}
			else {
				Integer fileNo = lastReport.getFileNo() + 1;
				iecgCheckReport.setFileNo(fileNo);
		}
	}
}
